/***************************************************************************
 *   Copyleft 2008-2009 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./vWin32MainWindow.h"
#include "../../Framework/vEngine3D.h"
#include "../../resource.h"

namespace Vendetta {
	vWin32MainWindow::vWin32MainWindow(const vString& name, vDeviceInfo* devInfo): vMainWindow(name, devInfo) {
		mIsInitialized = false;
		mIsFullscreen = false;
		mIsMinimized = false;
		if (mpDevInfo) {
			mWidth = mpDevInfo->CurrentDevMode()->dmPelsWidth;
			mHeight = mpDevInfo->CurrentDevMode()->dmPelsHeight;
			mIsFullscreen = mpDevInfo->IsFullScreen();
		}
		create();
	}
	
	
	vWin32MainWindow::~vWin32MainWindow() {
		kill();
	}

	vVoid vWin32MainWindow::Update() {
		MSG msg;

		while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
			if (msg.message == WM_QUIT)
				return;
			else {
				::TranslateMessage(&msg);
				::DispatchMessage(&msg);
			}
		}
	}

	vBool vWin32MainWindow::create() {
		WNDCLASSEX wcex;
		DWORD dwExStyle;
		DWORD dwStyle;

		mhInstance = ::GetModuleHandle(vNull);

		wcex.cbSize			= sizeof(WNDCLASSEX);
		wcex.style			= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC;
		wcex.lpfnWndProc	= (WNDPROC) staticWindowProc;
		wcex.cbClsExtra		= 0;
		wcex.cbWndExtra		= 0;
		wcex.hInstance		= mhInstance;
		wcex.hIcon			= ::LoadIcon(mhInstance, MAKEINTRESOURCE(IDI_VTEAMICONMEDIUM));
		wcex.hCursor		= ::LoadCursor(NULL, IDC_ARROW);
		wcex.hbrBackground	= (HBRUSH) ::GetStockObject(WHITE_BRUSH);
		wcex.lpszMenuName	= vNull;
		wcex.lpszClassName	= mTitle.ToConstCharPtr();
		wcex.hIconSm		= ::LoadIcon(mhInstance, MAKEINTRESOURCE(IDI_VTEAMICON));

		if (!::RegisterClassEx(&wcex)) {
			throw vFatalException("Couldn't register window class. In vWin32MainWindow::create.");
		}
		if (mIsFullscreen) {
			DEVMODE dmScreenSettings;
			if (mpDevInfo) {
				dmScreenSettings = *(mpDevInfo->CurrentDevMode());
			} else {
				//TODO change this from device info
				memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));	// Makes Sure Memory's Cleared
				dmScreenSettings.dmSize = sizeof(dmScreenSettings);		// Size Of The Devmode Structure
				dmScreenSettings.dmPelsWidth = mWidth;				// Selected Screen Width
				dmScreenSettings.dmPelsHeight = mHeight;				// Selected Screen Height
				dmScreenSettings.dmBitsPerPel = 32;					// Selected Bits Per Pixel
				dmScreenSettings.dmDisplayFrequency = 60;
				dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
			}
			// Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
			if (::ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
				throw vFatalException("The Requested Fullscreen Mode Is Not Supported By Your Video Card. Use Windowed Mode Instead");
			}
		}
		if (mIsFullscreen) {
			dwExStyle = WS_EX_APPWINDOW;
			dwStyle	= WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
		} else {
			dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
			dwStyle	= WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
		}

		mHWND = ::CreateWindowEx(
			dwExStyle,					// window extended style
			mTitle.ToConstCharPtr(),	// window class name
			mTitle.ToConstCharPtr(),	// window caption
			dwStyle,					// window style
			CW_USEDEFAULT,              // initial x position
			CW_USEDEFAULT,              // initial y position
			mWidth,//CW_USEDEFAULT,              // initial x size
			mHeight,//CW_USEDEFAULT,              // initial y size
			vNull,						// parent window handle
			vNull,                      // window menu handle
			mhInstance,					// program instance handle
			(vVoid *) vNull);           // creation parameters
		if (!mHWND) {
			kill();
			throw vFatalException("Couldn't create window due to some unknown errors. In vWin32MainWindow::create.");
		}

		::ShowWindow(mHWND, SW_SHOW);
		::SetForegroundWindow(mHWND);
		::SetFocus(mHWND);

		mIsInitialized = true;
		
		return true;
	}

	vBool vWin32MainWindow::kill() {
		if (!mIsInitialized) {
			return true;
		}

		mIsInitialized = false;

		if (mHWND && !::DestroyWindow(mHWND)) {
			throw vFatalException("Couldn't release WWND. In vWin32MainWindow::kill.");
			mHWND = vNull;
		}

		if (mIsFullscreen) {
			ChangeDisplaySettings(NULL, 0);
		}

		ShowCursor(TRUE);

		if (mhInstance && !::UnregisterClass(mTitle.ToConstCharPtr(), mhInstance)) {
			throw vFatalException("Couldn't unregister \"%s\" class. In vWin32MainWindow::kill.", mTitle.ToConstCharPtr());
			mhInstance = vNull;
		}

		return true;
	}

	LRESULT CALLBACK vWin32MainWindow::staticWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)	{
		if (vEngine3D::InstancePtr()->MainWindowPtr()) {
			return ((vWin32MainWindow*) vEngine3D::InstancePtr()->MainWindowPtr())->windowProc(hWnd, uMsg, wParam, lParam);
		} else {
			return DefWindowProc(hWnd, uMsg, wParam, lParam);
		}
	}

	LRESULT vWin32MainWindow::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
		vPoint q;

		switch (uMsg) {
		case WM_ACTIVATE:
			mIsMinimized = wParam != WA_ACTIVE;
			//if (cEngine::Input) {
			//	if (IsMinimized)
			//		cEngine::Input->disable();
			//	else
			//		cEngine::Input->enable();
			//}
			return 0;
		case WM_SYSCOMMAND:
			switch (wParam) {
				case SC_SCREENSAVE:
				case SC_MONITORPOWER:
					return 0;
			}
			break;
		case WM_CLOSE:
			PostQuitMessage(0);
			return 0;
		case WM_LBUTTONDOWN:
			if (vEngine3D::InstancePtr()->InputSystemPtr()) {
				if (vEngine3D::InstancePtr()->InputSystemPtr()->IsCaptureEnabled()) {
					::SetCapture(mHWND);
				}
				q.x = ((vShort) LOWORD (lParam));
				q.y = ((vShort) HIWORD (lParam));
				vEngine3D::InstancePtr()->InputSystemPtr()->FireMousePressedEvent(q, vMouseListener::V_MB_LEFT);
			}
			break;
		case WM_LBUTTONUP:
			if (vEngine3D::InstancePtr()->InputSystemPtr()) {
				if (vEngine3D::InstancePtr()->InputSystemPtr()->IsCaptureEnabled()) {
					::ReleaseCapture();
				}
				q.x = ((vShort) LOWORD (lParam));
				q.y = ((vShort) HIWORD (lParam));

				vEngine3D::InstancePtr()->InputSystemPtr()->FireMouseReleasedEvent(q, vMouseListener::V_MB_LEFT);
			}
			break;
		case WM_RBUTTONDOWN:
			if (vEngine3D::InstancePtr()->InputSystemPtr()) {
				if (vEngine3D::InstancePtr()->InputSystemPtr()->IsCaptureEnabled()) {
					::SetCapture(mHWND);
				}
				q.x = ((vShort) LOWORD (lParam));
				q.y = ((vShort) HIWORD (lParam));
				vEngine3D::InstancePtr()->InputSystemPtr()->FireMousePressedEvent(q, vMouseListener::V_MB_RIGHT);
			}
			break;
		case WM_RBUTTONUP:
			if (vEngine3D::InstancePtr()->InputSystemPtr()) {
				if (vEngine3D::InstancePtr()->InputSystemPtr()->IsCaptureEnabled()) {
					::ReleaseCapture();
				}
				q.x = ((vShort) LOWORD (lParam));
				q.y = ((vShort) HIWORD (lParam));

				vEngine3D::InstancePtr()->InputSystemPtr()->FireMouseReleasedEvent(q, vMouseListener::V_MB_RIGHT);
			}
			break;
		case WM_KEYDOWN:
			if (wParam == VK_ESCAPE) {
				vEngine3D::InstancePtr()->Quit();
			}
			if (vEngine3D::InstancePtr()->InputSystemPtr()) {
				vEngine3D::InstancePtr()->InputSystemPtr()->FireKeyboardPressedEvent(wParam);
			}
			break;
		case WM_KEYUP:
			if (vEngine3D::InstancePtr()->InputSystemPtr()) {
				vEngine3D::InstancePtr()->InputSystemPtr()->FireKeyboardReleasedEvent(wParam);
			}
			break;
		case WM_MOUSEMOVE:
			if (vEngine3D::InstancePtr()->InputSystemPtr()) {
				q.x = ((vShort) LOWORD (lParam));
				q.y = ((vShort) HIWORD (lParam));
				vEngine3D::InstancePtr()->InputSystemPtr()->FireMouseMovedEvent(q);
			}
			break;
		}

		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
}